/* test_md5.c
**
** Copyright (c) 2007, Meadhbh S. Hamrick
** All rights reserved.
** 
** Redistribution  and  use  in  source  and  binary  forms,  with  or  without
** modification, are permitted provided that the following conditions are met:
** 
**  * Redistributions of  source code must  retain the above  copyright notice,
**    this list of conditions and the following disclaimer.
** 
**  * Redistributions in binary form must reproduce the above copyright notice,
**    this list of conditions and the following disclaimer in the documentation
**    and/or other materials provided with the distribution.
** 
**  * Neither the  name of  Project Meadhbh  nor the names of  its contributors
**    may be used  to endorse  or promote  products derived from this  software
**    without specific prior written permission.
** 
** THIS SOFTWARE IS PROVIDED BY  THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY  EXPRESS OR IMPLIED WARRANTIES,  INCLUDING, BUT NOT  LIMITED TO, THE
** IMPLIED WARRANTIES  OF MERCHANTABILITY AND FITNESS FOR  A PARTICULAR PURPOSE
** ARE DISCLAIMED.  IN NO  EVENT SHALL THE  COPYRIGHT OWNER OR  CONTRIBUTORS BE
** LIABLE  FOR  ANY  DIRECT,   INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR
** CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT  LIMITED  TO,  PROCUREMENT  OF
** SUBSTITUTE GOODS  OR SERVICES;  LOSS OF USE,  DATA, OR PROFITS;  OR BUSINESS
** INTERRUPTION)  HOWEVER CAUSED  AND ON  ANY THEORY  OF LIABILITY,  WHETHER IN
** CONTRACT,  STRICT LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE OR  OTHERWISE)
** ARISING IN ANY WAY  OUT OF THE USE OF THIS SOFTWARE,  EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
** 
** $Id: test_md5.c 53 2007-09-01 07:48:29Z msh.mobile $
*/

/** \file test_md5.c
**  \brief Test MD5 Cryptographic Hash Functions
**
** This   file   implements   the    tests   that   are   referenced   in
** test_crypto.c.   It  is  assumed   that  the   initalization  function
** suite_md5_init()   will  be   called  before   any  of   the  test_*()
** functions. The initialization function does all the "heavy lifting" of
** creating  contexts  and hashing  test  vectors.  The individual  tests
** simply  compare output and  intermediate values  with known  good test
** values.
** 
** "Canonical"  test  vectors are  provided  in  the  inputs and  outputs
** arrays.  The inputs  array is  an array  of known  strings  that, when
** hashed, are supposed to generate  known values. We're using the values
** from RFC1321, it's as close to authoratative as we could find. To test
** the  hashing  functions,  the  suite_md5_init() function  hashes  each
** string in the inputs array and places the result in the actual_outputs
** array. In the test_md5_rfc_vectors()  function, we simply compare what
** we generated in the actual_outputs array with the known good values in
** the outputs array.
** 
** Numerous  tests for cloning  behavior are  performed. Cloning  is used
** extensively in  these tests.  If it doesn't  work, then  it's unlikely
** that anything  will work. But we perform  a number of tests  to see if
** there are particular problems with cloning before or after various md5
** function calls.
** 
** We test the mc_mic_md5_clear() function by clearing a used context and
** then  comparing it  with a  newly initialized  context. Note  that the
** clear function  simply reinitializes a  context to it's  default state
** rather than zeroing memory.
** 
** The  mc_mic_md5_initialize() function is  tested with  the known-value
** checks mentioned above. We also  verify that memory is properly zeroed
** out.
** 
** Finally,  the mc_mic_md5_update()  function  is tested  by looking  at
** intermediate and final results in md5 contexts.
*/

/* Macro Definitions */
#define MC_MIC_MD5_TEST_MAX_CONTEXTS 20
#define MC_MIC_MD5_TEST_MAX_VECTORS 7

/* File Includes */
#include <string.h>
#include "mutil.h"
#include "mcrypto/md5.h"
#include "test_md5.h"
#include <CUnit/CUnit.h>
#include <stdlib.h>
#include <stdio.h>

/* Typedefs, Structs, Unions, Enums, etc. */

/* Static Function Prototypes */

/* Variable Declarations */
tMCMd5 md5_actual_contexts [ MC_MIC_MD5_TEST_MAX_CONTEXTS ];
tMCMd5 md5_contexts [ MC_MIC_MD5_TEST_MAX_CONTEXTS ] = {
{
  .count = 0x0000000000000000LL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x0000000000000000LL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x0000000000000000LL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x0000000000000000LL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0xD41D8CD9,
    0x8F00B204,
    0xE9800998,
    0xECF8427E
  }
},
{
  .count = 0x0000000000000001LL,
  .data.asLongLong = {
    0x6100000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x0000000000000001LL,
  .data.asLongLong = {
    0x6100000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x0000000000000001LL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x0CC175B9,
    0xC0F1B6A8,
    0x31C399E2,
    0x69772661
  }
},
{
  .count = 0x0000000000000003LL,
  .data.asLongLong = {
    0x6162630000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x0000000000000003LL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x90015098,
    0x3CD24FB0,
    0xD6963F7D,
    0x28E17F72
  }
},
{
  .count = 0x000000000000000ELL,
  .data.asLongLong = {
    0x6D65737361676520LL,
    0x6469676573740000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x000000000000000ELL,
  .data.asLongLong = {
    0x6D65737361676520LL,
    0x6469676573740000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x000000000000000ELL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0xF96B697D,
    0x7CB7938D,
    0x525A2F31,
    0xAAF161D0
  }
},
{
  .count = 0x0000000000000010LL,
  .data.asLongLong = {
    0x6162636465666768LL,
    0x696A6B6C6D6E6F70LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x0000000000000010LL,
  .data.asLongLong = {
    0x6162636465666768LL,
    0x696A6B6C6D6E6F70LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x000000000000001ALL,
  .data.asLongLong = {
    0x6162636465666768LL,
    0x696A6B6C6D6E6F70LL,
    0x7172737475767778LL,
    0x797A000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x000000000000001ALL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0xC3FCD3D7,
    0x6192E400,
    0x7DFB496C,
    0xCA67E13B
  }
},
{
  .count = 0x0000000000000000LL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x000000000000001ALL,
  .data.asLongLong = {
    0x6162636465666768LL,
    0x696A6B6C6D6E6F70LL,
    0x7172737475767778LL,
    0x797A000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
},
{
  .count = 0x000000000000001ALL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0xC3FCD3D7,
    0x6192E400,
    0x7DFB496C,
    0xCA67E13B
  }
},
{
  .count = 0x0000000000000000LL,
  .data.asLongLong = {
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL,
    0x0000000000000000LL
  },
  .state = {
    0x67452301,
    0xEFCDAB89,
    0x98BADCFE,
    0x10325476
  }
}
};

uint8_t *md5_inputs [ MC_MIC_MD5_TEST_MAX_VECTORS ] = {
  (uint8_t *) "",
  (uint8_t *) "a",
  (uint8_t *) "abc",
  (uint8_t *) "message digest",
  (uint8_t *) "abcdefghijklmnopqrstuvwxyz",
  (uint8_t *) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
  (uint8_t *) "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
};

uint8_t md5_actual_outputs [ MC_MIC_MD5_TEST_MAX_VECTORS ][16];

uint8_t md5_outputs [ MC_MIC_MD5_TEST_MAX_VECTORS ][16] = {
    { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e },
    { 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 },
    { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 },
    { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 },
    { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b },
    { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f },
    { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a }
};

/* Function Definitions */

/** \fn int suite_md5_init( void )
**  \brief Calls various md5 functions in preparation for value checking.
**  \returns 0 (success)
** 
** This is  the function where "rubber  meets the road." We  call all the
** functions  under test  in this  initialization function  and  in later
** tests simply compare the values we get with known good values.
** 
** We have  a series of tMCMd5  data structures used to  hold the hashing
** context after  various operations. This function  generally performs a
** function,  then clones  the context.  We do  this to  save information
** about the intermediate state of the hash functions. This can help with
** debugging if something  goes wrong. IT IS NOT  THE GENERAL PATTERN FOR
** USING THIS API.
** 
** The algorithm-specific  functions don't  return error codes  and don't
** check parameters. Ergo, we won't  know if there's a failure during the
** init routine's run. We get that info later in the test_*()
** functions.
*/

int suite_md5_init() {
  /* Initialize the first context */
  mc_mic_md5_initialize( & (md5_actual_contexts[0]) );

  /* Clone a context that has not been used */
  mc_mic_md5_clone( & (md5_actual_contexts[1]), & (md5_actual_contexts[0]) ); 

  /* Clone a context for vector 0 */
  mc_mic_md5_clone( & (md5_actual_contexts[2]), & (md5_actual_contexts[0]) );

  /* Hash Nothing (vector 0) */
  mc_mic_md5_update( & (md5_actual_contexts[2]), (uint8_t *) md5_inputs[ 0 ], (uint32_t) strlen( (const char *) md5_inputs[ 0 ] ) );

  /* Clone this context */
  mc_mic_md5_clone( & (md5_actual_contexts[3]), & (md5_actual_contexts[2]) );

  /* Produce Digest for vector 0 */
  mc_mic_md5_digest( & (md5_actual_contexts[3]), (uint8_t *) md5_actual_outputs[0] );

  /* Clone a context for vector 1 */
  mc_mic_md5_clone( & (md5_actual_contexts[4]), & (md5_actual_contexts[0]) );

  /* Hash "a" (vector 1) */
  mc_mic_md5_update( & (md5_actual_contexts[4]), (uint8_t *) md5_inputs[1], (uint32_t) strlen( (const char *) md5_inputs[1] ) );

  /* Clone a context that has been updated with less than one block, but not
     finalized */
  mc_mic_md5_clone( & (md5_actual_contexts[5]), & (md5_actual_contexts[4]) );

  /* Clone the context for vector 1 prior to calling digest */
  mc_mic_md5_clone( & (md5_actual_contexts[6]), & (md5_actual_contexts[4]) );

  /* Produce digest for vector 1 */
  mc_mic_md5_digest( & (md5_actual_contexts[6]), (uint8_t *) md5_actual_outputs[1] );

  /* Clone a context for vector 2 */
  mc_mic_md5_clone( & (md5_actual_contexts[7]), & (md5_actual_contexts[0]) );

  /* Hash "abc" (vector 2) */
  mc_mic_md5_update( & (md5_actual_contexts[7]), (uint8_t *) md5_inputs[2], (uint32_t) strlen( (const char *) md5_inputs[2] ) );

  /* Clone the context for vector 2 prior to calling digest */
  mc_mic_md5_clone( & (md5_actual_contexts[8]), & (md5_actual_contexts[7]) );
		     
  /* Produce digest for vector 2 */
  mc_mic_md5_digest( & (md5_actual_contexts[8]), (uint8_t *) md5_actual_outputs[2] );

  /* Clone a context for vector 3 */
  mc_mic_md5_clone( & (md5_actual_contexts[9]), & (md5_actual_contexts[0]) );

  /* Hash "message digest" (vector 3) */
  mc_mic_md5_update( & (md5_actual_contexts[9]), (uint8_t *) md5_inputs[3], (uint32_t) strlen( (const char *) md5_inputs[3] ) );

  /* Clone a context that has been updated with exactly 14 bytes, but not
     finalized */
  mc_mic_md5_clone( & (md5_actual_contexts[10]), & (md5_actual_contexts[9]) );

  /* Clone the context for vector 3 prior to calling digest */
  mc_mic_md5_clone( & (md5_actual_contexts[11]), & (md5_actual_contexts[9]) );

  /* Produce digest for vector 3 */
  mc_mic_md5_digest( & (md5_actual_contexts[11]), (uint8_t *) md5_actual_outputs[3] );

  /* Clone a context for vector 4 */
  mc_mic_md5_clone( & (md5_actual_contexts[12]), & (md5_actual_contexts[0]) );

  /* Hash 16 bytes of "abcdefghijklmnopqrstuvwxyz" (vector 4) */
  mc_mic_md5_update( & (md5_actual_contexts[12]), (uint8_t *) md5_inputs[4], 16 );

  /* Clone a context that has been updated with exactly 16 bytes, but not
     finalized */
  mc_mic_md5_clone( & (md5_actual_contexts[13]), & (md5_actual_contexts[12]) );

  /* Clone a context to use for the rest of vector 4 */
  mc_mic_md5_clone( & (md5_actual_contexts[14]), & (md5_actual_contexts[12]) );

  /* Hash the remainder of vector 4 */
  mc_mic_md5_update( & (md5_actual_contexts[14]), (uint8_t *) & (md5_inputs[4][16]), (uint32_t) strlen( (const char *) &( md5_inputs[4][16] ) ) );

  /* Clone a context that has been updated twice, but not finalized */
  mc_mic_md5_clone( & (md5_actual_contexts[17]), & (md5_actual_contexts[14]) );

  /* Clone the context for vector 4 prior to calling digest */
  mc_mic_md5_clone( & (md5_actual_contexts[15]), & (md5_actual_contexts[14]) );

  /* Produce digest for vector 4 */
  mc_mic_md5_digest( & (md5_actual_contexts[15]), (uint8_t *) md5_actual_outputs[4] );

  /* Clone a context that has been finalized */
  mc_mic_md5_clone( & (md5_actual_contexts[18]), & (md5_actual_contexts[15]) );

  /* Clone a context for vector 5 */
  mc_mic_md5_clone( & (md5_actual_contexts[16]), & (md5_actual_contexts[0]) );

  /* Hash vector 5 */
  mc_mic_md5_update( & (md5_actual_contexts[16]), (uint8_t *) md5_inputs[5], (uint32_t) strlen( (const char *) md5_inputs[5] ) );

  /* Produce digest for vector 5 */
  mc_mic_md5_digest( & (md5_actual_contexts[16]), (uint8_t *) md5_actual_outputs[5] );

  /* Clone a context for vector 6 */
  mc_mic_md5_clone( & (md5_actual_contexts[16]), & (md5_actual_contexts[0]) );

  /* Hash vector 6 */
  mc_mic_md5_update( & (md5_actual_contexts[16]), (uint8_t *) md5_inputs[6], (uint32_t) strlen( (const char *) md5_inputs[6] ) );

  /* Produce digest for vector 6 */
  mc_mic_md5_digest( & (md5_actual_contexts[16]), (uint8_t *) md5_actual_outputs[6] );

  /* Clear context 16 */
  mc_mic_md5_clear( & (md5_actual_contexts[16]) );

  /* Clone a context that's been cleared */
  mc_mic_md5_clone( & (md5_actual_contexts[19]), &(md5_actual_contexts[16]) );

  return( 0 );
}

/** \fn void test_md5_rfc_vectors( void )
**  \brief Tests for compatibility with test vectors provided in RFC1321
** 
** During the init function's run, we hashed a number of "canonical" test
** vectors listed  in RFC1321.  In this function,  we simply  compare the
** results  of our  computation (in  the actual_outputs  array)  with the
** known-good values given in the RFC (in the outputs array).
*/

void test_md5_rfc_vectors( ) {

  CU_ASSERT_EQUAL( 0, memcmp( md5_outputs[0], md5_actual_outputs[0], 16 ) );
  CU_ASSERT_EQUAL( 0, memcmp( md5_outputs[1], md5_actual_outputs[1], 16 ) );
  CU_ASSERT_EQUAL( 0, memcmp( md5_outputs[2], md5_actual_outputs[2], 16 ) );
  CU_ASSERT_EQUAL( 0, memcmp( md5_outputs[3], md5_actual_outputs[3], 16 ) );
  CU_ASSERT_EQUAL( 0, memcmp( md5_outputs[4], md5_actual_outputs[4], 16 ) );
  CU_ASSERT_EQUAL( 0, memcmp( md5_outputs[5], md5_actual_outputs[5], 16 ) );
  CU_ASSERT_EQUAL( 0, memcmp( md5_outputs[6], md5_actual_outputs[6], 16 ) );
}

/** \fn void test_mc_mic_md5_clone( void )
**  \brief Tests our ability to clone hash contexts
** 
** In this  test, we  look to  see if our  ability to  copy a  context is
** dependent on  the state of the  context. I would be  very surprised if
** this fails.
*/

void test_mc_mic_md5_clone( ) {
  /* Can we clone a newly initialized context? */
  CU_ASSERT_EQUAL( 0, memcmp( & (md5_actual_contexts[0]), & (md5_actual_contexts[1]), sizeof( tMCMd5 ) ) );

  /* Can we clone a context that's been updated with less than one block? */
  CU_ASSERT_EQUAL( 0, memcmp( & (md5_actual_contexts[4]), & (md5_actual_contexts[5]), sizeof( tMCMd5 ) ) );
  
  /* Can we clone a context that's processed 14 bytes? */
  CU_ASSERT_EQUAL( 0, memcmp( & (md5_actual_contexts[9]), & (md5_actual_contexts[10]), sizeof( tMCMd5 ) ) );

  /* Can we clone a context that's processed 16 bytes? */
  CU_ASSERT_EQUAL( 0, memcmp( & (md5_actual_contexts[12]), & (md5_actual_contexts[13]), sizeof( tMCMd5 ) ) );

  /* Can we clone a context that's called update twice? */
  CU_ASSERT_EQUAL( 0, memcmp( & (md5_actual_contexts[14]), & (md5_actual_contexts[17]), sizeof( tMCMd5 ) ) );

  /* Can we clone a context that's been finalized? */
  CU_ASSERT_EQUAL( 0, memcmp( & (md5_actual_contexts[15]), & (md5_actual_contexts[18]), sizeof( tMCMd5 ) ) );

  /* Can we clone a context that's been cleared? */
  CU_ASSERT_EQUAL( 0, memcmp( & (md5_actual_contexts[0]), & (md5_actual_contexts[19]), sizeof( tMCMd5 ) ) );
}

/** \fn void test_mc_mic_md5_clear( void )
**  \brief Tests to ensure that sensitive data is cleared
** 
** We test  the mc_mic_md5_clear() function  by comparing the state  of a
** MD5 context  after a  call to mc_mic_md5_clear()  with the state  of a
** context  after a  call to  mc_mic_md5_initialize(). If  they're equal,
** then we're good.
*/

void test_mc_mic_md5_clear( ) {
  /* Compare context 0 (newly initialized) with context 16 (newly cleared) */
  CU_ASSERT_EQUAL( 0, memcmp( & (md5_actual_contexts[0]), & (md5_actual_contexts[16]), sizeof( tMCMd5 ) ) );
}

/** \fn void test_mc_mic_md5_initialize( void )
**  \brief Tests the ability to initiailze a newly allocated hashing context. 
*/

void test_mc_mic_md5_initialize( ) {
  /* Count should be zero */
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].count );

  /* Data values should be zero */
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].data.asLongLong[0] );
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].data.asLongLong[1] );
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].data.asLongLong[2] );
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].data.asLongLong[3] );
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].data.asLongLong[4] );
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].data.asLongLong[5] );
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].data.asLongLong[6] );
  CU_ASSERT_EQUAL( 0LL, md5_actual_contexts[0].data.asLongLong[7] );

  /* State values are well known */
  CU_ASSERT_EQUAL( 0x67452301, md5_actual_contexts[0].state[0] );
  CU_ASSERT_EQUAL( 0xEFCDAB89, md5_actual_contexts[0].state[1] );
  CU_ASSERT_EQUAL( 0x98BADCFE, md5_actual_contexts[0].state[2] );
  CU_ASSERT_EQUAL( 0x10325476, md5_actual_contexts[0].state[3] );
}

/** \fn void test_mc_mic_md5_update( void )
**  \brief Tests the hash update function
** 
** Testing  the update  function is  fairly straight-forward.  We  have a
** series  of  known-good values  for  MD5  contexts  at different  times
** throught the hashing  process. We simply compare the  test states with
** the known good values.
*/

void test_mc_mic_md5_update( ) {

#ifndef WORDS_BIGENDIAN
  uint32_t j;

  for( j = 0; j < 4; j++ ) {
    md5_actual_contexts[3].state[j] = MU_END32( md5_actual_contexts[3].state[j] );
    md5_actual_contexts[6].state[j] = MU_END32( md5_actual_contexts[6].state[j] );
    md5_actual_contexts[8].state[j] = MU_END32( md5_actual_contexts[8].state[j] );
    md5_actual_contexts[11].state[j] = MU_END32( md5_actual_contexts[11].state[j] );
    md5_actual_contexts[15].state[j] = MU_END32( md5_actual_contexts[15].state[j] );
    md5_actual_contexts[18].state[j] = MU_END32( md5_actual_contexts[18].state[j] );
  }

  for( j = 0; j < 8; j++ ) {
    md5_actual_contexts[4].data.asLongLong[j] = MU_END64( md5_actual_contexts[4].data.asLongLong[j] );
    md5_actual_contexts[5].data.asLongLong[j] = MU_END64( md5_actual_contexts[5].data.asLongLong[j] );

    md5_actual_contexts[7].data.asLongLong[j] = MU_END64( md5_actual_contexts[7].data.asLongLong[j] );

    md5_actual_contexts[9].data.asLongLong[j] = MU_END64( md5_actual_contexts[9].data.asLongLong[j] );
    md5_actual_contexts[10].data.asLongLong[j] = MU_END64( md5_actual_contexts[10].data.asLongLong[j] );
    md5_actual_contexts[12].data.asLongLong[j] = MU_END64( md5_actual_contexts[12].data.asLongLong[j] );
    md5_actual_contexts[13].data.asLongLong[j] = MU_END64( md5_actual_contexts[13].data.asLongLong[j] );
    md5_actual_contexts[14].data.asLongLong[j] = MU_END64( md5_actual_contexts[14].data.asLongLong[j] );
    md5_actual_contexts[15].data.asLongLong[j] = MU_END64( md5_actual_contexts[15].data.asLongLong[j] );
    md5_actual_contexts[16].data.asLongLong[j] = MU_END64( md5_actual_contexts[16].data.asLongLong[j] );
    md5_actual_contexts[16].data.asLongLong[j] = MU_END64( md5_actual_contexts[16].data.asLongLong[j] );
    md5_actual_contexts[17].data.asLongLong[j] = MU_END64( md5_actual_contexts[17].data.asLongLong[j] );
    md5_actual_contexts[18].data.asLongLong[j] = MU_END64( md5_actual_contexts[18].data.asLongLong[j] );
  }
#endif

  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[0] ), (uint8_t *) &( md5_contexts[0] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[1] ), (uint8_t *) &( md5_contexts[1] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[2] ), (uint8_t *) &( md5_contexts[2] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[3] ), (uint8_t *) &( md5_contexts[3] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[4] ), (uint8_t *) &( md5_contexts[4] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[5] ), (uint8_t *) &( md5_contexts[5] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[6] ), (uint8_t *) &( md5_contexts[6] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[7] ), (uint8_t *) &( md5_contexts[7] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[8] ), (uint8_t *) &( md5_contexts[8] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[9] ), (uint8_t *) &( md5_contexts[9] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[10] ), (uint8_t *) &( md5_contexts[10] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[11] ), (uint8_t *) &( md5_contexts[11] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[12] ), (uint8_t *) &( md5_contexts[12] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[13] ), (uint8_t *) &( md5_contexts[13] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[14] ), (uint8_t *) &( md5_contexts[14] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[15] ), (uint8_t *) &( md5_contexts[15] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[16] ), (uint8_t *) &( md5_contexts[16] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[17] ), (uint8_t *) &( md5_contexts[17] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[18] ), (uint8_t *) &( md5_contexts[18] ), sizeof( tMCMd5 ) ) );
  CU_ASSERT_EQUAL( 0, memcmp( (uint8_t *) &( md5_actual_contexts[19] ), (uint8_t *) &( md5_contexts[19] ), sizeof( tMCMd5 ) ) );
}
